package com.finance.scheduler.service;

import com.finance.base.constant.BaseConstants;
import com.finance.base.enums.PullStatusEnum;
import com.finance.scheduler.responsexml.PullData;
import com.finance.scheduler.requestxml.PullDataRequest;
import com.finance.scheduler.responsexml.PullLogistics;
import com.finance.scheduler.thread.PullCallable;
import com.finance.system.bean.pojo.RecomputeTask;
import com.finance.system.bean.pojo.User;
import com.finance.system.service.IConfigService;
import com.finance.system.service.IRecomputeTaskService;
import com.finance.util.DateUtil;
import com.finance.util.OKHTTPUtil;
import com.finance.util.encrypt.EncryptUtil;
import com.finance.util.xml.JaxbUtil;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.text.ParseException;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

/**
 * 运单拉取相关业务
 * Created by gyw on 2017/4/24.
 */
@Component
public class LogisticsDataPuller {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private IRecomputeTaskService recomputeTaskService;
    @Resource
    private LogisticsDataPullCallBacker logisticsDataPullCallBacker;
    @Resource
    private IConfigService configService;
    @Autowired
    private AsyncTaskExecutor taskExecutor;
    @Autowired
    private LogisticsDataHanler logisticsDataHanler;

    /**
     * 按次拉取 不再一次拉完
     *
     * @param  day
     */
    public void pullData(Calendar day) {
        this.pullData(day, null);
    }

    /**
     * 重新拉单
     *
     * @param recomputeTask
     */
    public void pullData(RecomputeTask recomputeTask) {
        try {
            this.pullData(null,recomputeTask);
        } catch (Exception e) {
            this.logger.error(e.getMessage(),e);
        }
    }

    private void pullData(Calendar day, RecomputeTask recomputeTask) {
        PullResult pullResult = null;
        if(null != recomputeTask){
            pullResult = this.multiPull(recomputeTask);
        }else{
            pullResult = this.multiPull(day);
        }
        List<PullLogistics> list = pullResult.getList();
        if (CollectionUtils.isEmpty(list) && !pullResult.isError() && recomputeTask != null) {
            recomputeTask.setPullStatus(PullStatusEnum.OK.getState());
            this.logger.info("拉取不到数据：{}", recomputeTask.getMonth());
            User user = new User();
            user.setId(0L);
            this.logger.info("标记任务：{}为拉取ok", recomputeTask.getMonth());
            this.recomputeTaskService.updateRecomputeTask(recomputeTask, user);
        }
        String mailNos = this.logisticsDataHanler.proceedData(list);
        if (StringUtils.isNotEmpty(mailNos)) {
            logger.debug("成功拉取并处理运单单号:{}", mailNos);
            List<String> wayBillNums = this.str2list(mailNos);
            if(wayBillNums.size() <= 1000){
                this.logisticsDataPullCallBacker.callback(mailNos);
            }else{
                this.logisticsDataPullCallBacker.multiCallback(wayBillNums);
            }
        }
    }

    private PullResult multiPull(Calendar day){
        //一天分为4个时段
        List<PullDataRequest> pullDataRequestList = new ArrayList<PullDataRequest>();
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTimeInMillis(day.getTimeInMillis());
        try {
            String date1Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 00:00:00");
            String date2Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 02:00:00");
            String date3Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 04:00:00");
            String date4Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 06:00:00");
            String date5Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 08:00:00");
            String date6Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 10:00:00");
            String date7Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 13:00:00");
            String date8Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 15:00:00");
            String date9Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 17:00:00");
            String date10Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 20:00:00");
            String date11Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 22:00:00");
            String date12Str = DateUtil.dateFormat(day.getTime(),"yyyy-MM-dd 23:59:59");
            pullDataRequestList.add(this.createPullDataRequest(date1Str,date2Str));
            pullDataRequestList.add(this.createPullDataRequest(date2Str,date3Str));
            pullDataRequestList.add(this.createPullDataRequest(date3Str,date4Str));
            pullDataRequestList.add(this.createPullDataRequest(date4Str,date5Str));
            pullDataRequestList.add(this.createPullDataRequest(date5Str,date6Str));
            pullDataRequestList.add(this.createPullDataRequest(date6Str,date7Str));
            pullDataRequestList.add(this.createPullDataRequest(date7Str,date8Str));
            pullDataRequestList.add(this.createPullDataRequest(date8Str,date9Str));
            pullDataRequestList.add(this.createPullDataRequest(date9Str,date10Str));
            pullDataRequestList.add(this.createPullDataRequest(date10Str,date11Str));
            pullDataRequestList.add(this.createPullDataRequest(date11Str,date12Str));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return this.multiPull(pullDataRequestList);
    }

    /**
     * 多线程去拉数据
     * @param recomputeTask
     * @return
     */
    private PullResult multiPull(RecomputeTask recomputeTask) {
        String monthStr = recomputeTask.getMonth();
        List<PullDataRequest> pullDataRequestList = new ArrayList<PullDataRequest>();
        try {
            Date monthDate = DateUtil.str2date(monthStr, DateUtil.YEAR_AND_MONTH_FORMAT);
            Calendar calendar = Calendar.getInstance();
            calendar.setTimeInMillis(monthDate.getTime());
            int days = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
            for (int i = 0; i < days; i++) {
                Calendar day = Calendar.getInstance();
                day.setTimeInMillis(calendar.getTimeInMillis());
                day.set(Calendar.DAY_OF_MONTH, i + 1);
                PullDataRequest pullDataRequest = this.createDataRequestByDay(day);
                pullDataRequestList.add(pullDataRequest);
            }
        } catch (ParseException e) {
            this.logger.error(e.getMessage(), e);
        }
        return this.multiPull(pullDataRequestList);
    }

    private PullResult multiPull(List<PullDataRequest> pullDataRequestList){
        PullResult pullResult = new PullResult();
        CountDownLatch countDownLatch = new CountDownLatch(pullDataRequestList.size());
        List<Future<PullResult>> futureList = new ArrayList<Future<PullResult>>();
        this.logger.info("拉单请求List：{}",String.valueOf(pullDataRequestList));
        for(PullDataRequest pullDataRequest : pullDataRequestList){
            Future<PullResult> future = this.taskExecutor.submit(new PullCallable(this,pullDataRequest));
            futureList.add(future);
        }
        this.logger.info("线程执行任务List：{}",String.valueOf(futureList));
        for(Future<PullResult> future : futureList){
            try {
                PullResult result = future.get();
                pullResult.join(result);
            } catch (ExecutionException e) {
                this.logger.error(e.getMessage(), e);
                pullResult.error = true;
            } catch (InterruptedException e) {
                this.logger.error(e.getMessage(), e);
                Thread.currentThread().interrupt();
                pullResult.error = true;
            } finally {
                countDownLatch.countDown();
            }
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            this.logger.error(e.getMessage(), e);
            Thread.currentThread().interrupt();
            pullResult.error = true;
        }
        return pullResult;
    }

    public PullResult pullLogistic(PullDataRequest pullDataRequest) {
        PullResult pullResult = new PullResult();
        String pullDataUrl = this.configService.findConfig(BaseConstants.SYSCONFIG_KEY_CHUYAN_OMS_PULL);//配置表获取运单拉取请求接口路径
        Map<String, String> param = new HashMap<String, String>();
        param.put("param", JaxbUtil.bean2xml(pullDataRequest));
        logger.info("线程拉单请求参数 线程名：{} ：{} ", Thread.currentThread().getName(), param.toString());
        String responseString = null;
        try {
            responseString = OKHTTPUtil.postForm(pullDataUrl, param);
        } catch (Exception e) {
            logger.error(e.getMessage(),e);
            pullResult.setError(true);
         }
        logger.info("拉取到的报文：{}", responseString);
        PullData pullData = null;
        try {
            pullData = JaxbUtil.converyToJavaBean(responseString, PullData.class);
        } catch (Exception e) {
            this.logger.info("结果转换错误：{}", e.getMessage());
            pullResult.setError(true);
        }
        if(pullData != null && "00".equals(pullData.getErrorCode())){
            if(pullData.getPullLogisticsList().size() > 0) {
                pullResult.addAll(pullData.getPullLogisticsList());
            }
        }else{
            pullResult.setError(true);
        }
        return pullResult;
    }

    /**
     * 按天
     *
     * @param calendar
     * @return
     */
    public PullDataRequest createDataRequestByDay(Calendar calendar) {
        Calendar from = Calendar.getInstance();
        from.setTimeInMillis(calendar.getTimeInMillis());
        from.set(Calendar.HOUR_OF_DAY, 0);
        from.set(Calendar.MINUTE, 0);
        from.set(Calendar.SECOND, 0);
        Calendar to = Calendar.getInstance();
        to.setTimeInMillis(calendar.getTimeInMillis());
        to.set(Calendar.HOUR_OF_DAY, 23);
        to.set(Calendar.MINUTE, 59);
        to.set(Calendar.SECOND, 59);
        return this.create(from, to);
    }

    /**
     * 按月份
     *
     * @param fetchDate
     * @return
     */
    public PullDataRequest createPullDataRequestByMonth(Date fetchDate) {
        Calendar from = Calendar.getInstance();
        from.setTime(fetchDate);
        from.set(Calendar.DAY_OF_MONTH, 1);
        from.set(Calendar.HOUR_OF_DAY, 0);
        from.set(Calendar.MINUTE, 0);
        from.set(Calendar.SECOND, 0);
        int lastDay = from.getActualMaximum(Calendar.DAY_OF_MONTH);
        Calendar to = Calendar.getInstance();
        to.setTime(fetchDate);
        to.set(Calendar.DAY_OF_MONTH, lastDay);
        to.set(Calendar.HOUR_OF_DAY, 23);
        to.set(Calendar.MINUTE, 59);
        to.set(Calendar.SECOND, 59);
        return this.create(from, to);
    }

    private PullDataRequest create(Calendar from, Calendar to) {
        String requestTime = DateUtil.dateFormat(new Date(), DateUtil.SIMPLE_SECOND_FORMAT);
        PullDataRequest pullDataRequest = new PullDataRequest();
        pullDataRequest.setBeginTime(DateUtil.dateFormat(new Date(from.getTimeInMillis())));
        pullDataRequest.setEndTime(DateUtil.dateFormat(new Date(to.getTimeInMillis())));
        String digestKey = this.configService.findConfig(BaseConstants.SYSCONFIG_KEY_CHUYAN_OMS_DIGISTKEY);//秘钥
        String charset = this.configService.findConfig(BaseConstants.SYSCONFIG_KEY_CHUYAN_OMS_CHARSET);//字符编码格式
        String str = requestTime + digestKey;
        pullDataRequest.setRequestTime(requestTime);
        pullDataRequest.setDigest(EncryptUtil.md5Base64(str, charset));
        pullDataRequest.setRequestNums(this.configService.findConfig(BaseConstants.SYSCONFIG_KEY_REQUEST_NUMS));//拉取单量
        pullDataRequest.setIsRepeat(BaseConstants.SYSCONFIG_KEY_NOREPEAT);//不重复
        return pullDataRequest;
    }

    private PullDataRequest create(Date from, Date to){
        Calendar calendar1 = Calendar.getInstance();
        calendar1.setTime(from);
        Calendar calendar2 = Calendar.getInstance();
        calendar2.setTime(to);
        return this.create(calendar1,calendar2);
    }

    private List<String> str2list(String mailNos){
        if(StringUtils.isBlank(mailNos)) {
            return new ArrayList<String>();
        }
        String[] wayBillNumS = mailNos.trim().split("\\,");
        return Arrays.asList(wayBillNumS);
    }

    private PullDataRequest createPullDataRequest(String from, String to) {
        String requestTime = DateUtil.dateFormat(new Date(), DateUtil.SIMPLE_SECOND_FORMAT);
        PullDataRequest pullDataRequest = new PullDataRequest();
        pullDataRequest.setBeginTime(from);
        pullDataRequest.setEndTime(to);
        String digestKey = this.configService.findConfig(BaseConstants.SYSCONFIG_KEY_CHUYAN_OMS_DIGISTKEY);//秘钥
        String charset = this.configService.findConfig(BaseConstants.SYSCONFIG_KEY_CHUYAN_OMS_CHARSET);//字符编码格式
        String str = requestTime + digestKey;
        pullDataRequest.setRequestTime(requestTime);
        pullDataRequest.setDigest(EncryptUtil.md5Base64(str, charset));
        pullDataRequest.setRequestNums(this.configService.findConfig(BaseConstants.SYSCONFIG_KEY_REQUEST_NUMS));//拉取单量
        pullDataRequest.setIsRepeat(BaseConstants.SYSCONFIG_KEY_NOREPEAT);//不重复
        return pullDataRequest;
    }

}
